home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 45
/
Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso
/
Aminet
/
gfx
/
x11
/
x3270_3_2_16.lha
/
amiga_src
/
host.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-30
|
18KB
|
820 lines
/*
* Copyright 1993, 1994, 1995, 1996, 1999, 2000 by Paul Mattes.
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*/
/*
* host.c
* This module handles the ibm_hosts file, connecting to and
* disconnecting from hosts, and state changes on the host
* connection.
*/
#include "globals.h"
#include "appres.h"
#include "resources.h"
#include "actionsc.h"
#include "hostc.h"
#include "macrosc.h"
#include "menubarc.h"
#include "popupsc.h"
#include "telnetc.h"
#include "trace_dsc.h"
#include "utilc.h"
#include "xioc.h"
#define RECONNECT_MS 2000 /* 2 sec before reconnecting to host */
#define RECONNECT_ERR_MS 5000 /* 5 sec before reconnecting to host */
#define MAX_RECENT 5
enum cstate cstate = NOT_CONNECTED;
Boolean std_ds_host = False;
Boolean non_tn3270e_host = False;
Boolean passthru_host = False;
#define LUNAME_SIZE 16
char luname[LUNAME_SIZE+1];
char *connected_lu = CN;
char *connected_type = CN;
Boolean ever_3270 = False;
char *current_host = CN;
char *full_current_host = CN;
unsigned short current_port;
#if defined(X3270_DISPLAY) /*[*/
char *reconnect_host = CN;
#endif /*]*/
struct host *hosts = (struct host *)NULL;
static struct host *last_host = (struct host *)NULL;
static Boolean auto_reconnect_inprogress = False;
static int net_sock = -1;
static void save_recent(const char *);
#if defined(X3270_DISPLAY) /*[*/
static void try_reconnect(void);
#endif /*]*/
int host_getnetsock(void)
{
return(net_sock);
}
static char *
stoken(char **s)
{
char *r;
char *ss = *s;
if (!*ss)
return NULL;
r = ss;
while (*ss && *ss != ' ' && *ss != '\t')
ss++;
if (*ss) {
*ss++ = '\0';
while (*ss == ' ' || *ss == '\t')
ss++;
}
*s = ss;
return r;
}
/*
* Read the host file
*/
void
hostfile_init(void)
{
FILE *hf;
char buf[1024];
static Boolean hostfile_initted = False;
struct host *h;
if (hostfile_initted)
return;
else
hostfile_initted = True;
if (appres.hostsfile == CN)
appres.hostsfile = xs_buffer("%s/ibm_hosts", LIBX3270DIR);
hf = fopen(appres.hostsfile, "r");
if (hf != (FILE *)NULL) {
while (fgets(buf, sizeof(buf), hf)) {
char *s = buf;
char *name, *entry_type, *hostname;
char *slash;
if (strlen(buf) > (unsigned)1 &&
buf[strlen(buf) - 1] == '\n') {
buf[strlen(buf) - 1] = '\0';
}
while (isspace(*s))
s++;
if (!*s || *s == '#')
continue;
name = stoken(&s);
entry_type = stoken(&s);
hostname = stoken(&s);
if (!name || !entry_type || !hostname) {
xs_warning("Bad %s syntax, entry skipped",
ResHostsFile);
continue;
}
h = (struct host *)Malloc(sizeof(*h));
h->name = NewString(name);
h->hostname = NewString(hostname);
/*
* Quick syntax extension to allow the hosts file to
* specify a port as host/port.
*/
if ((slash = strchr(h->hostname, '/')))
*slash = ':';
if (!strcmp(entry_type, "primary"))
h->entry_type = PRIMARY;
else
h->entry_type = ALIAS;
if (*s)
h->loginstring = NewString(s);
else
h->loginstring = CN;
h->prev = last_host;
h->next = (struct host *)NULL;
if (last_host)
last_host->next = h;
else
hosts = h;
last_host = h;
}
(void) fclose(hf);
}
/*
* Read the recent-connection file, and prepend it to the hosts list.
*/
save_recent(CN);
}
/*
* Look up a host in the list. Turns aliases into real hostnames, and
* finds loginstrings.
*/
static int
hostfile_lookup(const char *name, char **hostname, char **loginstring)
{
struct host *h;
hostfile_init();
for (h = hosts; h != (struct host *)NULL; h = h->next) {
if (h->entry_type == RECENT)
continue;
if (!strcmp(name, h->name)) {
*hostname = h->hostname;
*loginstring = h->loginstring;
return 1;
}
}
return 0;
}
#if defined(LOCAL_PROCESS) /*[*/
/* Recognize and translate "-e" options. */
static const char *
parse_localprocess(const char *s)
{
int sl = strlen(OptLocalProcess);
if (!strncmp(s, OptLocalProcess, sl)) {
if (s[sl] == ' ')
return(s + sl + 1);
else if (s[sl] == '\0') {
char *r;
r = getenv("SHELL");
if (r != CN)
return r;
else
return "/bin/sh";
}
}
return CN;
}
#endif /*]*/
/*
* Strip qualifiers from a hostname.
* Returns the hostname part in a newly-malloc'd string.
*/
static char *
split_host(char *s, Boolean *ansi, Boolean *std_ds, Boolean *passthru,
Boolean *non_e, char *xluname, char **port)
{
*ansi = False;
*std_ds = False;
*passthru = False;
*non_e = False;
*xluname = '\0';
*port = CN;
for (;;) {
char *at;
if (!strncmp(s, "a:", 2) || !strncmp(s, "A:", 2)) {
*ansi = True;
s += 2;
continue;
}
if (!strncmp(s, "s:", 2) || !strncmp(s, "S:", 2)) {
*std_ds = True;
s += 2;
continue;
}
if (!strncmp(s, "p:", 2) || !strncmp(s, "P:", 2)) {
*passthru = True;
s += 2;
continue;
}
if (!strncmp(s, "n:", 2) || !strncmp(s, "N:", 2)) {
*non_e = True;
s += 2;
continue;
}
if ((at = strchr(s, '@')) != NULL) {
if (at != s) {
int nc = at - s;
if (nc > LUNAME_SIZE)
nc = LUNAME_SIZE;
(void) strncpy(xluname, s, nc);
xluname[nc] = '\0';
}
s = at + 1;
continue;
}
break;
}
if (*s) {
char *r;
char *sep;
r = NewString(s);
sep = strrchr(r, ':');
if (sep == NULL)
sep = strrchr(r, ' ');
else if (strrchr(r, ' ') != NULL) {
Free(r);
return CN;
}
if (sep != CN) {
*sep++ = '\0';
while (*sep == ' ')
sep++;
}
if (port != (char **) NULL)
*port = sep;
return r;
} else
return CN;
}
/*
* Network connect/disconnect operations, combined with X input operations.
*
* Returns 0 for success, -1 for error.
* Sets 'reconnect_host', 'current_host' and 'full_current_host' as
* side-effects.
*/
int
host_connect(const char *n)
{
char nb[2048]; /* name buffer */
char *s; /* temporary */
const char *chost; /* to whom we will connect */
char *target_name;
char *ps = CN;
char *port = CN;
Boolean pending;
static Boolean ansi_host;
const char *localprocess_cmd = NULL;
if (CONNECTED || auto_reconnect_inprogress)
return 0;
/* Skip leading blanks. */
while (*n == ' ')
n++;
if (!*n) {
popup_an_error("Invalid (empty) hostname");
return -1;
}
/* Save in a modifiable buffer. */
(void) strcpy(nb, n);
/* Strip trailing blanks. */
s = nb + strlen(nb) - 1;
while (*s == ' ')
*s-- = '\0';
#if defined(X3270_DISPLAY) /*[*/
/* Remember this hostname, as the last hostname we connected to. */
if (reconnect_host != CN)
Free(reconnect_host);
reconnect_host = NewString(nb);
#endif /*]*/
/* Remember this hostname in the recent connection list and file. */
save_recent(nb);
#if defined(LOCAL_PROCESS) /*[*/
if ((localprocess_cmd = parse_localprocess(nb)) != CN) {
chost = localprocess_cmd;
port = appres.port;
} else
#endif /*]*/
{
/* Strip off and remember leading qualifiers. */
if ((s = split_host(nb, &ansi_host, &std_ds_host,
&passthru_host, &non_tn3270e_host, luname, &port)) == CN)
return -1;
/* Look up the name in the hosts file. */
if (hostfile_lookup(s, &target_name, &ps)) {
/*
* Rescan for qualifiers.
* Qualifiers, LU names, and ports are all overridden
* by the hosts file.
*/
Free(s);
if (!(s = split_host(target_name, &ansi_host,
&std_ds_host, &passthru_host, &non_tn3270e_host,
luname, &port)))
return -1;
}
chost = s;
/* Default the port. */
if (port == CN)
port = appres.port;
}
/*
* Store the original name in globals, even if we fail the connect
* later:
* current_host is the hostname part, stripped of qualifiers, luname
* and port number
* full_current_host is the entire string, for use in reconnecting
*/
if (n != full_current_host) {
if (full_current_host != CN)
Free(full_current_host);
full_current_host = NewString(n);
}
if (current_host != CN)
Free(current_host);
if (localprocess_cmd != CN) {
if (full_current_host[strlen(OptLocalProcess)] != '\0')
current_host = NewString(full_current_host +
strlen(OptLocalProcess) + 1);
else
current_host = NewString("default shell");
} else {
current_host = s;
}
/* Attempt contact. */
ever_3270 = False;
net_sock = net_connect(chost, port, localprocess_cmd != CN, &pending);
if (net_sock < 0) {
#if defined(X3270_DISPLAY) /*[*/
if (appres.once) {
/* Exit when the error pop-up pops down. */
exiting = True;
}
else if (appres.reconnect) {
auto_reconnect_inprogress = True;
(void) AddTimeOut(RECONNECT_ERR_MS, try_reconnect);
}
#endif /*]*/
/* Redundantly signal a disconnect. */
st_changed(ST_CONNECT, False);
return -1;
}
/* Success. */
/* Set pending string. */
if (ps != CN)
login_macro(ps);
/* Prepare Xt for I/O. */
x_add_input(net_sock);
/* Set state and tell the world. */
if (pending) {
cstate = PENDING;
st_changed(ST_HALF_CONNECT, True);
} else {
cstate = CONNECTED_INITIAL;
st_changed(ST_CONNECT, True);
#if defined(X3270_DISPLAY) /*[*/
if (appres.reconnect && error_popup_visible())
popdown_an_error();
#endif /*]*/
}
return 0;
}
#if defined(X3270_DISPLAY) /*[*/
/*
* Reconnect to the last host.
*/
static void
host_reconnect(void)
{
if (auto_reconnect_inprogress || current_host == CN ||
CONNECTED || HALF_CONNECTED)
return;
if (host_connect(reconnect_host) >= 0)
auto_reconnect_inprogress = False;
}
/*
* Called from timer to attempt an automatic reconnection.
*/
static void
try_reconnect(void)
{
auto_reconnect_inprogress = False;
host_reconnect();
}
#endif /*]*/
void
host_disconnect(Boolean failed)
{
if (CONNECTED || HALF_CONNECTED) {
x_remove_input();
net_disconnect();
net_sock = -1;
#if defined(X3270_DISPLAY) /*[*/
if (appres.once) {
if (error_popup_visible()) {
/*
* If there is an error pop-up, exit when it
* pops down.
*/
exiting = True;
} else {
/* Exit now. */
x3270_exit(0);
return;
}
} else if (appres.reconnect && !auto_reconnect_inprogress) {
/* Schedule an automatic reconnection. */
auto_reconnect_inprogress = True;
(void) AddTimeOut(failed? RECONNECT_ERR_MS:
RECONNECT_MS,
try_reconnect);
}
#endif /*]*/
/*
* Remember a disconnect from ANSI mode, to keep screen tracing
* in sync.
*/
#if defined(X3270_TRACE) /*[*/
if (IN_ANSI && toggled(SCREEN_TRACE))
trace_ansi_disc();
#endif /*]*/
cstate = NOT_CONNECTED;
/* Propagate the news to everyone else. */
st_changed(ST_CONNECT, False);
}
}
/* The host has entered 3270 or ANSI mode, or switched between them. */
void
host_in3270(enum cstate new_cstate)
{
Boolean now3270 = (new_cstate == CONNECTED_3270 ||
new_cstate == CONNECTED_SSCP ||
new_cstate == CONNECTED_TN3270E);
cstate = new_cstate;
ever_3270 = now3270;
st_changed(ST_3270_MODE, now3270);
}
void
host_connected(void)
{
cstate = CONNECTED_INITIAL;
st_changed(ST_CONNECT, True);
#if defined(X3270_DISPLAY) /*[*/
if (appres.reconnect && error_popup_visible())
popdown_an_error();
#endif /*]*/
}
/* Comparison function for the qsort. */
static int
host_compare(const void *e1, const void *e2)
{
const struct host *h1 = *(const struct host **)e1;
const struct host *h2 = *(const struct host **)e2;
int r;
if (h1->connect_time > h2->connect_time)
r = -1;
else if (h1->connect_time < h2->connect_time)
r = 1;
else
r = 0;
#if defined(CFDEBUG) /*[*/
printf("%s %ld %d %s %ld\n",
h1->name, h1->connect_time,
r,
h2->name, h2->connect_time);
#endif /*]*/
return r;
}
#if defined(CFDEBUG) /*[*/
static void
dump_array(const char *when, struct host **array, int nh)
{
int i;
printf("%s\n", when);
for (i = 0; i < nh; i++) {
printf(" %15s %ld\n", array[i]->name, array[i]->connect_time);
}
}
#endif /*]*/
/* Save the most recent host in the recent host list. */
static void
save_recent(const char *hn)
{
char *lcf_name = CN;
FILE *lcf = (FILE *)NULL;
struct host *h;
struct host *rest = (struct host *)NULL;
int n_ent = 0;
struct host *h_array[(MAX_RECENT * 2) + 1];
int nh = 0;
int i, j;
time_t t = time((time_t *)NULL);
/* Allocate a new entry. */
if (hn != CN) {
h = (struct host *)Malloc(sizeof(*h));
h->name = NewString(hn);
h->hostname = NewString(hn);
h->entry_type = RECENT;
h->loginstring = CN;
h->connect_time = t;
h_array[nh++] = h;
}
/* Put the existing entries into the array. */
for (h = hosts; h != (struct host *)NULL; h = h->next) {
if (h->entry_type != RECENT)
break;
h_array[nh++] = h;
}
/* Save the ibm_hosts entries for later. */
rest = h;
if (rest != (struct host *)NULL)
rest->prev = (struct host *)NULL;
/*
* Read the last-connection file, to capture the any changes made by
* other instances of x3270.
*/
if (appres.connectfile_name != CN &&
strcasecmp(appres.connectfile_name, "none")) {
lcf_name = do_subst(appres.connectfile_name, True, True);
lcf = fopen(lcf_name, "r");
}
if (lcf != (FILE *)NULL) {
char buf[1024];
while (fgets(buf, sizeof(buf), lcf) != CN) {
int sl;
time_t connect_time;
char *ptr;
/* Pick apart the entry. */
sl = strlen(buf);
if (buf[sl - 1] == '\n')
buf[sl-- - 1] = '\0';
if (!sl ||
buf[0] == '#' ||
(connect_time = strtoul(buf, &ptr, 10)) == 0L ||
ptr == buf ||
*ptr != ' ' ||
!*(ptr + 1))
continue;
h = (struct host *)Malloc(sizeof(*h));
h->name = NewString(ptr + 1);
h->hostname = NewString(ptr + 1);
h->entry_type = RECENT;
h->loginstring = CN;
h->connect_time = connect_time;
h_array[nh++] = h;
if (nh > (MAX_RECENT * 2) + 1)
break;
}
fclose(lcf);
}
/* Sort the array, in reverse order by connect time. */
#if defined(CFDEBUG) /*[*/
dump_array("before", h_array, nh);
#endif /*]*/
qsort(h_array, nh, sizeof(struct host *), host_compare);
#if defined(CFDEBUG) /*[*/
dump_array("after", h_array, nh);
#endif /*]*/
/*
* Filter out duplicate host names, and limit the array to
* MAX_RECENT entries total.
*/
hosts = (struct host *)NULL;
last_host = (struct host *)NULL;
for (i = 0; i < nh; i++) {
h = h_array[i];
if (h == (struct host *)NULL)
continue;
h->next = (struct host *)NULL;
if (last_host != (struct host *)NULL)
last_host->next = h;
h->prev = last_host;
last_host = h;
if (hosts == (struct host *)NULL)
hosts = h;
n_ent++;
/* Zap the duplicates. */
for (j = i+1; j < nh; j++) {
if (h_array[j] &&
(n_ent >= MAX_RECENT ||
!strcmp(h_array[i]->name, h_array[j]->name))) {
#if defined(CFDEBUG) /*[*/
printf("%s is a dup of %s\n",
h_array[j]->name, h_array[i]->name);
#endif /*]*/
Free(h_array[j]->name);
Free(h_array[j]->hostname);
Free(h_array[j]);
h_array[j] = (struct host *)NULL;
}
}
}
/* Re-attach the ibm_hosts entries to the end. */
if (rest != (struct host *)NULL) {
if (last_host != (struct host *)NULL) {
last_host->next = rest;
} else {
hosts = rest;
}
rest->prev = last_host;
}
/* If there's been a change, rewrite the file. */
if (hn != CN &&
appres.connectfile_name != CN &&
strcasecmp(appres.connectfile_name, "none")) {
lcf = fopen(lcf_name, "w");
if (lcf != (FILE *)NULL) {
fprintf(lcf, "# Created %s# by %s\n", ctime(&t), build);
for (h = hosts; h != (struct host *)NULL; h = h->next) {
if (h->entry_type != RECENT)
break;
(void) fprintf(lcf, "%lu %s\n", h->connect_time,
h->name);
}
}
fclose(lcf);
Free(lcf_name);
}
}
/* Support for state change callbacks. */
struct st_callback {
struct st_callback *next;
void (*func)(Boolean);
};
static struct st_callback *st_callbacks[N_ST];
static struct st_callback *st_last[N_ST];
/* Register a function interested in a state change. */
void
register_schange(int tx, void (*func)(Boolean))
{
struct st_callback *st;
st = (struct st_callback *)Malloc(sizeof(*st));
st->func = func;
st->next = (struct st_callback *)NULL;
if (st_last[tx] != (struct st_callback *)NULL)
st_last[tx]->next = st;
else
st_callbacks[tx] = st;
st_last[tx] = st;
}
/* Signal a state change. */
void
st_changed(int tx, Boolean mode)
{
struct st_callback *st;
for (st = st_callbacks[tx];
st != (struct st_callback *)NULL;
st = st->next) {
(*st->func)(mode);
}
}
/* Explicit connect/disconnect actions. */
void
Connect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
action_debug(Connect_action, event, params, num_params);
if (check_usage(Connect_action, *num_params, 1, 1) < 0)
return;
if (CONNECTED || HALF_CONNECTED) {
popup_an_error("Already connected");
return;
}
(void) host_connect(params[0]);
/*
* If called from a script and the connection was successful (or
* half-successful), pause the script until we are connected and
* we have identified the host type.
*/
if (!w && (CONNECTED || HALF_CONNECTED))
sms_connect_wait();
}
#if defined(X3270_MENUS) /*[*/
void
Reconnect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
action_debug(Reconnect_action, event, params, num_params);
if (check_usage(Reconnect_action, *num_params, 0, 0) < 0)
return;
if (CONNECTED || HALF_CONNECTED) {
popup_an_error("Already connected");
return;
}
if (current_host == CN) {
popup_an_error("No previous host to connect to");
return;
}
host_reconnect();
/*
* If called from a script and the connection was successful (or
* half-successful), pause the script until we are connected and
* we have identified the host type.
*/
if (!w && (CONNECTED || HALF_CONNECTED))
sms_connect_wait();
}
#endif /*]*/
void
Disconnect_action(Widget w unused, XEvent *event, String *params,
Cardinal *num_params)
{
action_debug(Disconnect_action, event, params, num_params);
if (check_usage(Disconnect_action, *num_params, 0, 0) < 0)
return;
host_disconnect(False);
}